#include <asm-i386/page.h>
#include <linux/bootmem.h>
#include <asm-i386/pgtable.h>
#include <asm-i386/processor.h>
#include <linux/mmzone.h>
#include <asm-i386/dma.h>
#include <asm-i386/io.h>
#include <linux/mm.h>
#include <asm-i386/e820.h>
#include <linux/debug.h>
pgd_t swapper_pg_dir[1024] __attribute__((__aligned__(PAGE_SIZE)));
static unsigned long totalram_pages;
void  zone_sizes_init(void)
{
    unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
    unsigned int max_dma, low;

    max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT;//计算最大可能DMA寻址的PFN
    low = max_low_pfn;

    if (low < max_dma)
        zones_size[ZONE_DMA] = low;
    else {
        zones_size[ZONE_DMA] = max_dma;
        zones_size[ZONE_NORMAL] = low - max_dma;
    }
    free_area_init(zones_size);
}

static void  set_max_mapnr_init(void)
{
    max_mapnr = num_mappedpages = num_physpages = max_low_pfn;
}

static inline int page_is_ram (unsigned long pagenr)
{
    int i;

    for (i = 0; i < e820.nr_map; i++) {
        unsigned long addr, end;

        if (e820.map[i].type != E820_RAM)	/* not usable memory */
            continue;
        /*
         *	!!!FIXME!!! Some BIOSen report areas as RAM that
         *	are not. Notably the 640->1Mb area. We need a sanity
         *	check here.
         */
        addr = (e820.map[i].addr+PAGE_SIZE-1) >> PAGE_SHIFT;
        end = (e820.map[i].addr+e820.map[i].size) >> PAGE_SHIFT;
        if  ((pagenr >= addr) && (pagenr < end))
            return 1;
    }
    return 0;
}

static int  free_pages_init(void)
{
    //extern int ppro_with_ram_bug(void);
    int  reservedpages, pfn;

    //bad_ppro = ppro_with_ram_bug();

    /* this will put all low memory onto the freelists */
    totalram_pages += free_all_bootmem();

    reservedpages = 0;
    for (pfn = 0; pfn < max_low_pfn; pfn++) {
        /*
         * Only count reserved RAM pages
         */
        if (page_is_ram(pfn) && PageReserved(mem_map+pfn))
            reservedpages++;
    }
    return reservedpages;
}

static void pagetable_init (void)
{
    unsigned long vaddr, end;
    pgd_t *pgd, *pgd_base;
    int i, j, k;
    pmd_t *pmd;
    pte_t *pte, *pte_base;

    /*
     * This can be zero as well - no problem, in that case we exit
     * the loops anyway due to the PTRS_PER_* conditions.
     */
    end = (unsigned long)__va(max_low_pfn*PAGE_SIZE);
    pgd_base = swapper_pg_dir;
    i = __pgd_offset(PAGE_OFFSET);
    pgd = pgd_base + i;
    for (; i < PTRS_PER_PGD; pgd++, i++) {
        vaddr = i * PGDIR_SIZE;
        if (end && (vaddr >= end))
            break;
        pmd = (pmd_t *)pgd;
        for (j = 0; j < PTRS_PER_PMD; pmd++, j++) {
            vaddr = i*PGDIR_SIZE + j*PMD_SIZE;
            if (end && (vaddr >= end))
                break;
            pte_base = pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);

            for (k = 0; k < PTRS_PER_PTE; pte++, k++) {
                vaddr = i*PGDIR_SIZE + j*PMD_SIZE + k*PAGE_SIZE;
                if (end && (vaddr >= end))
                    break;
                *pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL);
            }
            set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte_base)));
            if (pte_base != pte_offset(pmd, 0)) {
                BUG();
            }
        }
    }
}
void  paging_init(void)
{
    int i=0;
    pagetable_init();
    load_cr3(swapper_pg_dir);
    __flush_tlb();
    i++;
    zone_sizes_init();
}

void  mem_init(void) {

    if (!mem_map) {
        //BUG();
    }

    set_max_mapnr_init();

    high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);

    /* clear the zero-page */

    free_pages_init();

//    codesize = (unsigned long) &_etext - (unsigned long) &_text;
//    datasize = (unsigned long) &_edata - (unsigned long) &_etext;
//    initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin;

//    printk( "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %ldk highmem)\n",
//            (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
//            max_mapnr << (PAGE_SHIFT-10),
//            codesize >> 10,
//            reservedpages << (PAGE_SHIFT-10),
//            datasize >> 10,
//            initsize >> 10,
//            (unsigned long) (totalhigh_pages << (PAGE_SHIFT-10))
//    );
}